package cn.mall.framework.wxsdk.core.api;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import lombok.extern.slf4j.Slf4j;
import cn.mall.framework.wxsdk.aes.AesException;
import cn.mall.framework.wxsdk.aes.SHA1;
import cn.mall.framework.wxsdk.core.exception.WxRespException;
import cn.mall.framework.wxsdk.core.kit.HttpRequest;
import cn.mall.framework.wxsdk.core.kit.JsonMsgBuilder;
import cn.mall.framework.wxsdk.core.msg.Article2;
import cn.mall.framework.wxsdk.core.msg.FollowList;
import cn.mall.framework.wxsdk.core.msg.Follower;
import cn.mall.framework.wxsdk.core.msg.Group;
import cn.mall.framework.wxsdk.core.msg.WeixinAccount;
import cn.mall.framework.wxsdk.core.msg.Menu;
import cn.mall.framework.wxsdk.core.msg.OutPutMsg;
import cn.mall.framework.wxsdk.core.msg.Template;
import cn.mall.framework.wxsdk.core.profile.WxApiUrl;
import cn.mall.framework.wxsdk.core.profile.WxMsgType;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;

/**
 * 微信公众平台开发者API接口实现
 */
@Slf4j
public class WxApiHandler implements WxApi {


    private WeixinAccount mpAct;

    /**
     * 微信公众平台接口构建
     * @param mpAct 公众号信息
     */
    public WxApiHandler(WeixinAccount mpAct) {
    	this.mpAct = mpAct;
    }
    
    public WeixinAccount getMpAct(){
    	return this.mpAct;
    }
    
    @Override
    public String getAccessToken() throws WxRespException {
        String token = mpAct.getAccessToken();
        if (null == token
                || token.isEmpty()
                || mpAct.getExpiresIn() < System.currentTimeMillis()) {
            synchronized (mpAct){
                refreshAccessToken();
            }
            token = mpAct.getAccessToken();
        }
        return token;
    }

    @Override
    public void refreshAccessToken() throws WxRespException {
        String url = String.format(WxApiUrl.ACCESS_TOKEN_API,
                mpAct.getAppId(),
                mpAct.getAppSecret());
        String result = "";
        try {
            result = HttpRequest.get(url);
            log.info("获取微信公众号access_token:\n%s",result);
        } catch (IOException e) {
            log.error("刷新ACCESS_TOKEN时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        mpAct.createAccessToken(result);
    }

    @Override
    public List<String> getServerIp() throws WxRespException {
        String url = String.format(WxApiUrl.IP_LIST_API, getAccessToken());
        String result = "";
        try {
            result = HttpRequest.get(url);
        } catch (IOException e) {
            log.error("获取微信服务器IP时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        JSONObject tmp = JSON.parseObject(result);
        List<String> ips = JSONObject.parseArray(tmp.getString("ip_list"), String.class);
        return ips;
    }

    @Override
    public List<Menu> getMenu() throws WxRespException {
        String url = String.format(WxApiUrl.CUSTOM_MENU_API, "get", getAccessToken());
        String result = "";
        try {
            result = HttpRequest.get(url);
        } catch (IOException e) {
            log.error("获取当前自定义菜单时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        JSONObject tmp = JSON.parseObject(result).getJSONObject("menu");
        List<Menu> menus = JSON.parseArray(tmp.getString("button"), Menu.class);
        return menus;
    }

    @Override
    public boolean createMenu(Menu... menus) throws WxRespException {
        PropertyFilter null_filter = new PropertyFilter() {
            @Override
            public boolean apply(Object object, String name, Object value) {
                if(name.equals("key")
                        && (null==value || "".equals(value))) {
                    return false;
                }
                if (name.equals("url")
                        && (null==value || "".equals(value))) {
                    return false;
                }
                return true;
            }
        };
        Map<String, Object> buttons = new HashMap<String, Object>();
        buttons.put("button", Arrays.asList(menus));
        String btn_json = JSONObject.toJSONString(buttons, null_filter);
        String url = String.format(WxApiUrl.CUSTOM_MENU_API, "create", getAccessToken());
        String result = "";
        try {
        	log.info("正在创建微信自定义菜单。。。\n%s",btn_json);
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, btn_json);
        } catch (IOException e) {
            log.error("创建自定义菜单时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        return true;
    }

    @Override
    public boolean deleteMenu() throws WxRespException {
        String url = String.format(WxApiUrl.CUSTOM_MENU_API, "delete", getAccessToken());
        String result = "";
        try {
            result = HttpRequest.get(url);
        } catch (IOException e) {
            log.error("删除微信自定义菜单时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        return true;
    }

    @Override
    public int createGroup(String name) throws WxRespException {
        String url = String.format(WxApiUrl.GROUPS_API, "create", getAccessToken());
        String data = "{\"group\":{\"name\":\""+name+"\"}}";
        String result = "";
        try {
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, data);
        } catch (IOException e) {
            log.error("创建微信分组时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        JSONObject tmp = jsonResult.getJSONObject("group");
        return tmp.getInteger("id");
    }

    @Override
    public List<Group> getGroups() throws WxRespException {
        String url = String.format(WxApiUrl.GROUPS_API, "get", getAccessToken());
        String result = "";
        try {
            result = HttpRequest.get(url);
        } catch (IOException e) {
            log.error("获取所有分组时失败!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        String tmp = JSON.parseObject(result).getString("groups");
        List<Group> groups = JSON.parseArray(tmp, Group.class);
        return groups;
    }

    @Override
    public boolean renGroup(int id, String name) throws WxRespException {
        String url = String.format(WxApiUrl.GROUPS_API, "update", getAccessToken());
        name = name.length() > 30 ? name.substring(0,30) : name;
        String data = "{\"group\":{\"id\":"+id+",\"name\":\""+name+"\"}}";
        String result = "";
        try {
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, data);
        } catch (IOException e) {
            log.error("修改分组名称时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        return true;
    }

    @Override
    public int getGroupId(String openId) throws WxRespException {
        String url = String.format(WxApiUrl.GROUPS_API, "getid", getAccessToken());
        String data = "{\"openid\":\""+openId+"\"}";
        String result = "";
        try {
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, data);
        } catch (IOException e) {
            log.error("获取用户所在分组时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        int group_id = JSON.parseObject(result).getInteger("groupid");
        return group_id;
    }

    @Override
    public boolean move2Group(String openId, int groupId) throws WxRespException {
        String url = String.format(WxApiUrl.GROUPS_USER_API, getAccessToken());
        String data = "{\"openid\":\""+openId+"\",\"to_groupid\":"+groupId+"}";
        String result = "";
        try {
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, data);
        } catch (IOException e) {
            log.error("移动用户分组时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        return true;
    }

    @Override
    public FollowList getFollowerList(String nextOpenId) throws WxRespException {
        String url = String.format(WxApiUrl.FOLLOWS_USER_API, getAccessToken(), nextOpenId);
        String result = "";
        try {
            result = HttpRequest.get(url);
            log.info("获取微信公众号关注者列表\n地址:	%s\n结果:	%s",url,result);
        } catch (IOException e) {
            log.error("获取关注用户列表时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        FollowList followers = JSON.parseObject(result, FollowList.class);
        String openid = JSON.parseObject(result).getJSONObject("data").getString("openid");
        List<String> openids = JSON.parseArray(openid, String.class);
        followers.setOpenIds(openids);
        return followers;
    }

    @Override
    public Follower getFollower(String openId) throws WxRespException {
        String url = String.format(WxApiUrl.USER_INFO_API, getAccessToken(), openId);
        String result = "";
        try {
            result = HttpRequest.get(url);
            log.info("获取微信公众号关注者信息\n地址:	%s\n结果:	%s",url,result);
        } catch (IOException e) {
            log.error("获取用户信息时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        Follower follower = JSON.parseObject(result, Follower.class);
        return follower;
    }

    @Override
    public boolean sendCustomerMsg(OutPutMsg msg) throws WxRespException {
        String url = String.format(WxApiUrl.CUSTOM_MESSAGE_API, getAccessToken());
        String custom_msg = "";
        WxMsgType type = WxMsgType.valueOf(msg.getMsgType());
        switch (type) {
            case text:
                custom_msg = JsonMsgBuilder.create().text(msg).build();
                break;
            case image:
                custom_msg = JsonMsgBuilder.create().image(msg).build();
                break;
            case voice:
                custom_msg = JsonMsgBuilder.create().voice(msg).build();
                break;
            case video:
                custom_msg = JsonMsgBuilder.create().video(msg).build();
                break;
            case music:
                custom_msg = JsonMsgBuilder.create().music(msg).build();
                break;
            case news:
                custom_msg = JsonMsgBuilder.create().news(msg).build();
                break;
            default:
                break;
        }
        String result = "";
        try {
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, custom_msg);
        } catch (IOException e) {
            log.error("发送客服消息时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        return true;
    }

    @Override
    public boolean sendTemplateMsg(String openId, String templateId,
                                   String topColor, String url, Template... templates) throws WxRespException {
        String api_url = String.format(WxApiUrl.TEMPLATE_MESSAGE_API, getAccessToken());
        String template_msg = JsonMsgBuilder.create()
                .template(openId, templateId, topColor, url, templates)
                .build();
        String result = "";
        try {
            result = HttpRequest.post(api_url, HttpRequest.APPLICATION_JSON, template_msg);
        } catch (IOException e) {
            log.error("发送模板消息时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        return true;
    }

    @Override
    public String upMedia(String mediaType, File file) throws WxRespException {
        String url = String.format(WxApiUrl.MEDIA_UP_API, mediaType, getAccessToken());
        String result = "";
        try {
            result = HttpRequest.upload(url, file);
        } catch (IOException e) {
            log.error("上传多媒体文件时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        String media_id = JSON.parseObject(result).getString("media_id");
        return media_id;
    }

    @Override
    public void dlMedia(String mediaId, File file) throws WxRespException {
        String url = String.format(WxApiUrl.MEDIA_DL_API, getAccessToken(), mediaId);
        // 检查文件夹是否存在
        if(!file.exists()) {
            String path = file.getAbsolutePath();
            String separ = System.getProperties().getProperty("file.separator");
            File dir = new File(path.substring(0, path.lastIndexOf(separ)));
            dir.mkdirs();
        }
        try {
            HttpRequest.download(url, file);
        } catch (IOException e) {
            log.error("下载多媒体文件时出现异常!!!", e);
        }
    }

    @Override
    public String[] upNews(Article2... articles2s) throws WxRespException {
        String url = String.format(WxApiUrl.NEWS_UPLOAD_API, getAccessToken());
        String upnews_msg = JsonMsgBuilder.create().uploadNews(articles2s).build();
        String result = "";
        try {
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, upnews_msg);
        } catch (IOException e) {
            log.error("上传群发图文消息时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        JSONObject tmp = JSON.parseObject(result);
        String[] results = {
                tmp.getString("type"),
                tmp.getString("media_id"),
                tmp.getString("created_at")
        };
        return results;
    }

    @Override
    public String[] upVideo(String mediaId,
                                String title, String description) throws WxRespException {
        String url = String.format(WxApiUrl.MEDIA_UPVIDEO_API, getAccessToken());
        String result = "";
        String upvideo_msg = JsonMsgBuilder.create().uploadVideo(mediaId,title,description).build();
        try {
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, upvideo_msg);
        } catch (IOException e) {
            log.error("上传群发消息中的视频时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        JSONObject tmp = JSON.parseObject(result);
        String[] results = {
                tmp.getString("type"),
                tmp.getString("media_id"),
                tmp.getString("created_at")
        };
        return results;
    }

    @Override
    public String sendAll(OutPutMsg msg) throws WxRespException {
        String group_id = msg.getGroupId();
        List<String> to_users = msg.getToUsers();
        if (null != group_id
                && !group_id.isEmpty()
                && !to_users.isEmpty()) {
            throw new RuntimeException("群发消息只能选择一种模式");
        }
        String url = "";
        if (to_users.isEmpty()) {
           url =  String.format(WxApiUrl.GROUP_NEWS_MESSAGE_API, "sendall", getAccessToken());
        } else {
            url = String.format(WxApiUrl.GROUP_NEWS_MESSAGE_API, "send", getAccessToken());
        }
        String result = "";
        String send_msg = JsonMsgBuilder.create().sendAll(msg).build();
        try {
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, send_msg);
        } catch (IOException e) {
            log.error("发送群消息时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        JSONObject tmp = JSON.parseObject(result);
        return tmp.getString("msg_id");
    }

    @Override
    public boolean delSendAll(String msgId) throws WxRespException {
        String url = String.format(WxApiUrl.GROUP_NEWS_MESSAGE_API, "delete", getAccessToken());
        String result = "";
        String del_msg = "{\"msg_id\":"+ msgId +"}";
        try {
            result = HttpRequest.post(url, HttpRequest.APPLICATION_JSON, del_msg);
        } catch (IOException e) {
            log.error("删除群发消息时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        return true;
    }

    @Override
    public Map<String, Object> getJsAPISign(String url) throws WxRespException {
        // 1.创建JSTICKET
        String ticket = mpAct.getJsTicket();
        if (null == ticket
                || ticket.isEmpty()
                || mpAct.getJsExpiresIn() < System.currentTimeMillis()) {
            synchronized (mpAct){
                refreshJsAPITicket();
            }
            ticket = mpAct.getJsTicket();
        }
        // 2.生成签名
        String sign_param = "jsapi_ticket=%1$s&noncestr=%2$s&timestamp=%3$s&url=%4$s";
        String nonce = UUID.randomUUID().toString().toLowerCase();
        long time = System.currentTimeMillis() / 1000;
        String sign = null;
        try {
            sign = SHA1.calculate(String.format(sign_param, ticket, nonce, time, url));
        } catch (AesException e) {
            log.error("生成JSTICKET的签名时出现异常!!!", e);
        }
        // 3.返回参数与值
        Map<String, Object> sign_map = new HashMap<String, Object>();
        sign_map.put("url", url);
        sign_map.put("ticket", ticket);
        sign_map.put("nonce", nonce);
        sign_map.put("timestamp", time);
        sign_map.put("sign", sign);
        return sign_map;
    }

    @Override
    public void refreshJsAPITicket() throws WxRespException {
        String token_url = WxApiUrl.JSAPI_TICKET_URL+getAccessToken();
        String result = "";
        try {
            result = HttpRequest.get(token_url);
        } catch (IOException e) {
            log.error("刷新JSTICKET时出现异常!!!", e);
        }
        JSONObject jsonResult = JSON.parseObject(result);
        if (null != jsonResult.getInteger("errcode") && jsonResult.getInteger("errcode") != 0) {
            throw new WxRespException(result);
        }
        mpAct.createJsTicket(result);
    }
}
